package com.qiudao.commonutil.util;

import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Description:  与日期、时间操作有关的工具类。
 *  注意：某些方法会将输入时间转换成格林威治时间，时区默认为东八区。可以通过方法 <code>DateUtil#setTimeZone</code>改变默认时区。
 *
 * @author: gdc
 * @date: 2019/9/1
 * @version 1.0
 */
public class DateUtil {
    private static final String DEFAULT_PATTERN = "yyyy-MM-dd";

    /**
     * 默认使用的Locale。默认Locale为 US
     */
    private static final Locale DEFAULT_LOCALE = Locale.US;
    // ~ 静态属性及初始化 /////////////////////////////////////////////

    /**
     * 默认的时区，一般是使用东八区。即比标准的格林威治时间快8个小时。
     */
    private static TimeZone TIME_ZONE = TimeZone.getDefault();

    /**
     * ISO8601基本日期格式 yyyyMMdd ，如：20021225 代表2002年12月25日。
     */
    public static final String ISO_DATE_FORMAT = "yyyyMMdd";

    /**
     * ISO8601扩展日期格式 yyyy-MM-dd ，如：2002-12-25 代表2002年12月25日。
     */
    public static final String ISO_EXPANDED_DATE_FORMAT = "yyyy-MM-dd";

    public static final String MONTH_FORMAT = "yyyy-MM";

    /**
     * ISO8601基本时间格式 HHmmssSSSzzz ，如：143212333-0500 代表西5区，14点32分12秒333毫秒 24小时制
     */
    public static final String ISO_TIME_FORMAT = "HHmmssSSSzzz";

    /**
     * ISO8601基本时间格式 HH:mm:ss,SSSzzz ，如：14:32:12,333-0500 代表西5区，14点32分12秒333毫秒 24小时制
     */
    public static final String ISO_EXPANDED_TIME_FORMAT = "HH:mm:ss,SSSzzz";

    /**
     * ISO8601基本日期格式 yyyyMMddTHHmmssSSSzzz ，如：20021225T143212333-0500 代表西5区 2002年12月25日 14点32分12秒333毫秒
     */
    public static final String ISO_DATE_TIME_FORMAT = "yyyyMMdd'T'HHmmssSSSzzz";

    /**
     * ISO8601基本日期格式 yyyy-MM-ddTHH:mm:ss,SSSzzz ，如：2002-12-25T14:32:12,333-0500 代表西5区 2002年12月25日 14点32分12秒333毫秒
     */
    public static final String ISO_EXPANDED_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss,SSSzzz";

    /**
     * 基本时间格式 HHmmss ，如：143212 代表14点32分12秒 24小时制
     */
    public static final String TIME_FORMAT = "HHmmss";

    /**
     * 基本时间格式 HH:mm:ss ，如：14:32:12 代表14点32分12秒 24小时制
     */
    public static final String EXPANDED_TIME_FORMAT = "HH:mm:ss";

    /**
     * 日期格式 yyyyMMdd HHmmss ，如：20021225 143212 代表 2002年12月25日 14点32分12秒
     */
    public static final String DATE_TIME_FORMAT = "yyyyMMdd HHmmss";

    /**
     * 日期格式 yyyy-MM-dd HH:mm:ss ，如：2002-12-25 14:32:12 代表 2002年12月25日 14点32分12秒
     */
    public static final String EXPANDED_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    /**
     * 日期格式 yyyy-MM-dd HH:mm ，如：2002-12-25 14:32 代表 2002年12月25日 14点32分0秒
     */
    public static final String EXPANDED_DATE_TIME_MINUTE_FORMAT = "yyyy-MM-dd HH:mm";

    /**
     * 西方国家的日期格式 MMM dd, yyyy ，如：February 3, 1982 代表1982年2月3日
     */
    public static final String ENGLISH_DATE = "MMM dd, yyyy";

    /**
     * 西方国家的时间格式 MMM dd, yyyy H:mm:ss.SSS ，如：February 3, 1982 12:12:12.005 代表1982年2月3日 12点12分12秒5毫秒
     */
    public static final String ENGLISH_DATE_TIME = "MMM dd, yyyy H:mm:ss.SSS";

    /**
     * 美国日期格式 yyyy.MM.dd G 'at' HH:mm:ss z ，如：2001.07.04 AD at 12:08:56 PDT 代表2001年7月4日 12点8分56秒
     */
    public static final String US_DATE_TIME_1 = "yyyy.MM.dd G 'at' HH:mm:ss z";

    /**
     * 美国日期格式 yyyyy.MMMMM.dd GGG hh:mm aaa ，如：02001.July.04 AD 12:08 PM 代表2001年7月4日 12点8分
     */
    public static final String US_DATE_TIME_2 = "yyyyy.MMMMM.dd GGG hh:mm aaa";

    /**
     * 美国日期格式 EEE, d MMM yyyy HH:mm:ss Z ，如：Wed, 4 Jul 2001 12:08:56 -0700 代表2001年7月4日 12点8分56秒 星期三
     */
    public static final String US_DATE_TIME_3 = "EEE, d MMM yyyy HH:mm:ss Z";

    // 第1年1月1日 (格里高利历)
    public static final int JAN_1_1_JULIAN_DAY = 1721426;

    // 1970年1月1日 (格里高利历)
    public static final int EPOCH_JULIAN_DAY = 2440588;

    public static final int EPOCH_YEAR = 1970;

    // 一周、一天、一小时、一分钟、一秒，换算成毫秒的全局变量。
    public static final int ONE_SECOND = 1000;

    public static final int ONE_MINUTE = 60 * ONE_SECOND;

    public static final int ONE_HOUR = 60 * ONE_MINUTE;

    public static final long ONE_DAY = 24 * ONE_HOUR;

    public static final long ONE_WEEK = 7 * ONE_DAY;

    // ~ 方法 ////////////////////////////////////////////////////////////////

    public static void main(String[] args) {
        Date date = DateUtil.basicStringToDate("20190401");
        System.out.println(DateUtil.getDate(date));
    }

    /**
     * 设置系统默认的<code>java.util.TimeZone</code>
     *
     * @param timeZone <code>java.util.TimeZone</code>
     */
    public static void setTimeZone(TimeZone timeZone) {
        TIME_ZONE = timeZone;
    }

    public static Date toDateTime(String isoDateString) {
        return stringToDate(isoDateString, EXPANDED_DATE_TIME_FORMAT);
    }

    public static Date toDate(String isoDateString) {
        return stringToDate(isoDateString, ISO_EXPANDED_DATE_FORMAT);
    }

    public static Date stringToDate(String isoDateString) {
        return stringToDate(isoDateString, EXPANDED_DATE_TIME_FORMAT);
    }

    // ISO8601基本日期格式
    public static Date basicStringToDate(String isoDateString) {
        return stringToDate(isoDateString, ISO_DATE_FORMAT);
    }

    public static Date stringToDate(String isoDateString, String pattern) {
        if (StringUtils.isBlank(pattern)) {
            return null;
            // throw new IllegalArgumentException("匹配模式不能为空！");
        }

        return stringToDate(isoDateString, new SimpleDateFormat(pattern));
    }

    /**
     * 把格式为yyyy-MM-dd的字符串转换为日期
     *
     * @param dateString
     * @return
     */
    public static Date stringToDateByDay(String dateString) {
        if (StringUtils.isBlank(dateString)) {
            return null;
        }

        return stringToDate(dateString, new SimpleDateFormat(DEFAULT_PATTERN));
    }

    /**
     * 把格式为yyyy-MM的字符串转换为日期
     *
     * @param dateString
     * @return
     */
    public static Date stringToDateByMonth(String dateString) {
        if (StringUtils.isBlank(dateString)) {
            return null;
        }

        return stringToDate(dateString, new SimpleDateFormat(MONTH_FORMAT));
    }

    public static Date stringToDate(String isoDateString, SimpleDateFormat simpleDateFormat) {
        if (StringUtils.isBlank(isoDateString)) {
            // throw new IllegalArgumentException("日期字符串不能为空！");
            return null;
        }
        try {
            return simpleDateFormat.parse(isoDateString);
        } catch (ParseException e) {
            // throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 将指定日期转换为字符串
     *
     * @param date
     * @param pattern
     * @param locale
     * @return
     */
    public static String formatDate(Date date, String pattern, Locale locale) {
        pattern = (StringUtils.isEmpty(pattern)) ? DEFAULT_PATTERN : pattern;
        locale = (null == locale) ? DEFAULT_LOCALE : locale;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, locale);
        return simpleDateFormat.format(date);
    }

    public static String getDateTime(Date date) {
        return format(date, EXPANDED_DATE_TIME_FORMAT);
    }

    public static String getDate(Date date) {
        return format(date, ISO_EXPANDED_DATE_FORMAT);
    }

    public static String getDATEFORMAT(Date date) {
        return format(date, ISO_DATE_FORMAT);
    }

    public static String format(Date date, String pattern) {
        return format(date, new SimpleDateFormat(pattern));
    }

    /**
     * 将<code>java.util.Date</code>按照给定的模式转换成日期字符串。
     *
     * @param date      <code>java.util.Date</code>
     * @param formatter 给定的模式
     * @return 转换后的日期字符串
     */
    public static String getDate(Date date, SimpleDateFormat formatter) {
        if (date == null || formatter == null) {
            return null;
        }
        formatter.setTimeZone(TIME_ZONE);
        return formatter.format(date);
    }

    /**
     * 将<code>java.util.Date</code>按照给定的模式转换成日期字符串。
     *
     * @param date      <code>java.util.Date</code>
     * @param formatter 给定的模式
     * @return 转换后的日期字符串
     */
    public static String format(Date date, SimpleDateFormat formatter) {
        return getDate(date, formatter);
    }

    // 获取年份
    public static String getYear(Date date) {
        return format(date, ISO_EXPANDED_DATE_FORMAT).split("-")[0];
    }

    // 获取月份
    public static String getMonth(Date date) {
        return format(date, ISO_EXPANDED_DATE_FORMAT).split("-")[1];
    }

    /**
     * 返回yyyy-MM
     *
     * @param date
     * @return
     */
    public static String getYearMonth(Date date) {
        return format(date, MONTH_FORMAT);
    }

    // 获取日期
    public static String getDay(Date date) {
        return format(date, ISO_EXPANDED_DATE_FORMAT).split("-")[2];
    }

    // 获取时分秒
    public static String getTime(Date date) {
        return format(date, EXPANDED_DATE_TIME_FORMAT).split(" ")[1];
    }

    /**
     * 确定给定的年份是否为闰年。
     *
     * <pre>
     * </pre>
     *
     * @param date <code>java.util.Date</code>
     * @return True 如果是闰年
     */
    public static boolean isLeapYear(Date date) {
        if (date == null) {
            throw new RuntimeException("输入日期不能为空！");
        }
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);

        return cal.isLeapYear(cal.get(Calendar.YEAR));
    }


    public static int getHour(Date date) {
        if (date == null) {
            return -1;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }

    /**
     * 根据日历的规则，为给定的日历字段添加或减去指定的时间量。
     *
     * @param date   <code>java.util.Date</code>
     * @param field  给定的日历字段
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date add(Date date, int field, int amount) {
        if (date == null) {
            return null;
        }
        Calendar cal = GregorianCalendar.getInstance(TIME_ZONE);
        cal.setTime(date);
        cal.add(field, amount);

        return cal.getTime();
    }

    /**
     * 增加或减少指定数量的年份。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addYears(Date date, int amount) {
        return add(date, Calendar.YEAR, amount);
    }

    /**
     * 增加或减少指定数量的月份。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addMonths(Date date, int amount) {
        return add(date, Calendar.MONTH, amount);
    }

    /**
     * 修改周，时间按周的量增加或减少。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addWeeks(Date date, int amount) {
        return add(date, Calendar.WEEK_OF_YEAR, amount);
    }

    /**
     * 增加或减少指定数量的日数。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addDays(Date date, int amount) {
        return add(date, Calendar.DAY_OF_MONTH, amount);
    }

    /**
     * 增加或减少指定数量的小时数。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addHours(Date date, int amount) {
        return add(date, Calendar.HOUR_OF_DAY, amount);
    }

    /**
     * 增加或减少指定数量的分钟数。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addMinutes(Date date, int amount) {
        return add(date, Calendar.MINUTE, amount);
    }

    /**
     * 增加或减少指定数量的秒钟数。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addSeconds(Date date, int amount) {
        return add(date, Calendar.SECOND, amount);
    }

    /**
     * 增加或减少指定数量的毫秒数。
     *
     * @param date   <code>java.util.Date</code>
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date addMilliseconds(Date date, int amount) {
        return add(date, Calendar.MILLISECOND, amount);
    }

    /**
     * 在给定的日历字段上添加或减去（上/下）单个时间单元，不更改更大的字段。
     *
     * @param date  <code>java.util.Date</code>
     * @param field 给定的日历字段
     * @param up    添加或减去（上/下）
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date roll(Date date, int field, boolean up) {
        Calendar cal = GregorianCalendar.getInstance(TIME_ZONE);
        cal.setTime(date);
        cal.roll(field, up);

        return cal.getTime();
    }

    /**
     * 向指定日历字段添加指定（有符号的）时间量，不更改更大的字段。
     *
     * @param date   <code>java.util.Date</code>
     * @param field  给定的日历字段
     * @param amount 数量
     * @return 运算后的 <code>java.util.Date</code>
     */
    public static Date roll(Date date, int field, int amount) {
        Calendar cal = GregorianCalendar.getInstance(TIME_ZONE);
        cal.setTime(date);
        cal.roll(field, amount);

        return cal.getTime();
    }

    /**
     * 将毫秒时间戳转换成格里高利历的日数。日数是只从格里高利历第1年1月1日算起的日数。
     *
     * @param millis 给定的毫秒时间戳
     * @return 格里高利历的日数
     */
    public static int millisToJulianDay(long millis) {
        return EPOCH_JULIAN_DAY - JAN_1_1_JULIAN_DAY + (int) (millis / ONE_DAY);
    }

    /**
     * 将<code>java.util.Date</code>转换成格里高利历的日数。 日数是只从格里高利历第1年1月1日算起的日数。
     *
     * @param date <code>java.util.Date</code>
     * @return 格里高利历的日数
     */
    public static int dateToJulianDay(Date date) {
        return millisToJulianDay(date.getTime());
    }

    /**
     * 计算两个日期之间相差的秒钟数。如果第一个日期在第二个日期之前，则返回正，反之返回负。
     *
     * @param early 第一个日期
     * @param late  第二个日期
     * @return 两个日期之间相差的秒钟数
     */
    public static int secondsBetween(Date early, Date late) {
        return (int) ((late.getTime() / ONE_SECOND) - (early.getTime() / ONE_SECOND));
    }

    /**
     * 计算两个日期之间相差的分钟数。如果第一个日期在第二个日期之前，则返回正，反之返回负。
     *
     * @param early 第一个日期
     * @param late  第二个日期
     * @return 两个日期之间相差的分钟数
     */
    public static int minutesBetween(Date early, Date late) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(early);
        c2.setTime(late);

        return (int) ((late.getTime() / ONE_MINUTE) - (early.getTime() / ONE_MINUTE));
    }

    /**
     * 计算两个日期之间相差的小时数。如果第一个日期在第二个日期之前，则返回正，反之返回负。
     *
     * @param early 第一个日期
     * @param late  第二个日期
     * @return 两个日期之间相差的小时数
     */
    public static int hoursBetween(Date early, Date late) {
        return (int) ((late.getTime() / ONE_HOUR) - (early.getTime() / ONE_HOUR));
    }

    /**
     * 计算两个日期之间相差的天数。如果第一个日期在第二个日期之前，则返回正，反之返回负。
     *
     * @param early 第一个日期
     * @param late  第二个日期
     * @return 两个日期之间相差的天数
     */
    public static int daysBetween(Date early, Date late) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(early);
        c2.setTime(late);
        return dateToJulianDay(c2.getTime()) - dateToJulianDay(c1.getTime());
    }

    /**
     * 计算两个日期之间相差的月数。如果第一个日期在第二个日期之前，则返回正，反之返回负。
     *
     * @param early 第一个日期
     * @param late  第二个日期
     * @return 两个日期之间相差的月数
     */
    public static int monthsBetween(Date early, Date late) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(early);
        c2.setTime(late);
        return ((c2.get(Calendar.YEAR) * 12) + c2.get(Calendar.MONTH)) - ((c1.get(Calendar.YEAR) * 12) + c1.get(Calendar.MONTH));
    }

    /**
     * 计算两个日期之间相差的年数。如果第一个日期在第二个日期之前，则返回正，反之返回负。
     *
     * @param early 第一个日期
     * @param late  第二个日期
     * @return 两个日期之间相差的年数
     */
    public static int yearsBetween(Date early, Date late) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(early);
        c2.setTime(late);
        return c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
    }

    /**
     * 将格里高利历日数转换成 <code>java.sql.Date</code>
     *
     * @param julian 格里高利历日数
     * @return <code>java.sql.Date</code>
     */
    public static java.sql.Date julianDayToSqlDate(long julian) {
        return new java.sql.Date(julianDayToMillis(julian));
    }

    /**
     * 将格里高利历日数转换成 <code>java.util.Date</code>
     *
     * @param julian 格里高利历日数
     * @return <code>java.util.Date</code>
     */
    public static Date julianDayToDate(long julian) {
        return new Date(julianDayToMillis(julian));
    }

    /**
     * 将格里高利历日数转换成毫秒时间戳。
     *
     * @param julian 格里高利历日数
     * @return 毫秒时间戳
     */
    public static long julianDayToMillis(long julian) {
        return (julian - EPOCH_JULIAN_DAY + JAN_1_1_JULIAN_DAY) * ONE_DAY;
    }

    /**
     * 将日历转换成儒略历日期。算法参见： <a href="http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html">Julian Day Calculations
     * (Gregorian Calendar)</a>, 由 Bill Jeffrys 提供。
     *
     * @param c 日历实例
     * @return 儒略历日期
     */
    public static float toJulian(Calendar c) {
        int Y = c.get(Calendar.YEAR);
        int M = c.get(Calendar.MONTH);
        int D = c.get(Calendar.DATE);
        int A = Y / 100;
        int B = A / 4;
        int C = 2 - A + B;
        float E = (int) (365.25f * (Y + 4716));
        float F = (int) (30.6001f * (M + 1));
        float JD = (C + D + E + F) - 1524.5f;

        return JD;
    }

    /**
     * 将日期转换成儒略历日期。算法参见： <a href="http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html">Julian Day Calculations
     * (Gregorian Calendar)</a>, 由 Bill Jeffrys 提供。
     *
     * @param date 日期
     * @return 儒略历日期
     */
    public static float toJulian(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);

        return toJulian(c);
    }

    protected static float normalizedJulian(float JD) {
        float f = Math.round(JD + 0.5f) - 0.5f;

        return f;
    }

    /**
     * 给定此 <code>java.util.Date</code> 的时间值，返回指定日历字段可能拥有的最大值。
     *
     * @param date  <code>java.util.Date</code>
     * @param field 日历字段
     * @return 对于此 <code>java.util.Date</code> 的时间值而言，给定日历字段的最大值
     */
    public static Date getActualMaximum(Date date, int field) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(field, c.getActualMaximum(field));

        return c.getTime();
    }

    /**
     * 给定此 <code>java.util.Date</code> 的时间值，返回指定日历字段可能拥有的最小值。
     *
     * @param date  <code>java.util.Date</code>
     * @param field 日历字段
     * @return 对于此 <code>java.util.Date</code> 的时间值而言，给定日历字段的最小值
     */
    public static Date getActualMinimum(Date date, int field) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(field, c.getActualMinimum(field));

        return c.getTime();
    }

    /**
     * 截取日期，只保留年、月、日部分。
     *
     * @param date <code>java.util.Date</code>
     * @return 截取后的<code>java.util.Date</code>
     */
    public static Date trunc(Date date) {
        if (date != null) {
            Calendar c1 = Calendar.getInstance();
            c1.setTime(date);
            Calendar c2 = (Calendar) c1.clone();
            c1.clear();
            c1.set(Calendar.YEAR, c2.get(Calendar.YEAR));
            c1.set(Calendar.MONTH, c2.get(Calendar.MONTH));
            c1.set(Calendar.DAY_OF_MONTH, c2.get(Calendar.DAY_OF_MONTH));
            return c1.getTime();
        }
        return null;
    }

    /**
     * 返回当天的最后一秒
     *
     * @param date <code>java.util.Date</code>
     * @return 截取后的<code>java.util.Date</code>
     */
    public static Date dayEnd(Date date) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(date);
        Calendar c2 = (Calendar) c1.clone();
        c1.clear();
        c1.set(Calendar.YEAR, c2.get(Calendar.YEAR));
        c1.set(Calendar.MONTH, c2.get(Calendar.MONTH));
        c1.set(Calendar.DAY_OF_MONTH, c2.get(Calendar.DAY_OF_MONTH));
        // 23:59:59
        c1.set(Calendar.HOUR_OF_DAY, 23);
        c1.set(Calendar.MINUTE, 59);
        c1.set(Calendar.SECOND, 59);
        return c1.getTime();
    }

    /**
     * 判断两个日期是否是同一天，精确到天。
     *
     * @param date1 <code>java.util.Date</code>
     * @param date2 <code>java.util.Date</code>
     * @return true 如果两个日期是同一天
     */
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new RuntimeException("日期不能为空！");
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return isSameDay(cal1, cal2);
    }

    /**
     * 判断两个日期是否是同一天，精确到天。
     *
     * @param cal1 <code>java.util.Calendar</code>
     * @param cal2 <code>java.util.Calendar</code>
     * @return true 如果两个日期是同一天
     */
    public static boolean isSameDay(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new RuntimeException("日期不能为空！");
        }
        return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
    }

    /**
     * 判断两个时间是否完全相等，精确到毫秒。
     *
     * @param date1 <code>java.util.Date</code>
     * @param date2 <code>java.util.Date</code>
     * @return true 如果两个时间完全相等
     */
    public static boolean isSameLocalTime(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new RuntimeException("日期不能为空！");
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return isSameLocalTime(cal1, cal2);
    }

    /**
     * 判断两个时间是否完全相等，精确到毫秒。
     *
     * @param cal1 <code>java.util.Calendar</code>
     * @param cal2 <code>java.util.Calendar</code>
     * @return true 如果两个时间完全相等
     */
    public static boolean isSameLocalTime(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new RuntimeException("日期不能为空！");
        }
        return (cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)
                && cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR)
                && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
                && cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1.getClass() == cal2.getClass());
    }


    /**
     * 解析毫秒时间格式
     *
     * @param millis
     * @return
     */
    public static Date parseMillis(Long millis) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        return calendar.getTime();
    }

    /**
     * 返回date所在月的第一天
     *
     * @return
     */
    public static Date getFirstDayOfMonth(Date date) {
        String str = DateUtil.getYear(date) + "-" + DateUtil.getMonth(date) + "-01";
        return DateUtil.stringToDate(str, DateUtil.ISO_EXPANDED_DATE_FORMAT);
    }

    /**
     * 返回date所在月的最后一天
     *
     * @return
     */
    public static Date getLastDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int maxDate = cal.getActualMaximum(Calendar.DATE);
        cal.set(Calendar.DATE, maxDate);
        return cal.getTime();
    }

    // 取最近的时间
    public static Date getMaxDate(Date lastDate, Date newDate) {
        if (lastDate != null && newDate != null) {
            if (newDate.after(lastDate)) {
                return newDate;
            } else {
                return lastDate;
            }
        } else if (lastDate != null) {
            return lastDate;
        } else {
            return newDate;
        }
    }

    // 取最老的时间
    public static Date getMinDate(Date lastDate, Date newDate) {
        if (lastDate != null && newDate != null) {
            if (newDate.before(lastDate)) {
                return newDate;
            } else {
                return lastDate;
            }
        } else if (lastDate != null) {
            return lastDate;
        } else {
            return newDate;
        }
    }

    /**
     * 将字符串转换为指定格式的日期
     *
     * @param dateStr
     * @param pattern
     * @return
     */
    public static Date castStrToDate(String dateStr, String pattern) {
        Date date = null;

        if (dateStr == null || pattern == null) {

            return null;
        }

        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, DEFAULT_LOCALE);
            date = simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date; // null if parse faile
    }

    public static Date castStrToDate(String str) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        String pattern = "yyyy/MM/dd HH:mm:ss";
        if (str != null && str.indexOf("-") != -1) {
            pattern = "yyyy-MM-dd HH:mm:ss";
        }
        return castStrToDate(str, pattern);
    }

    /**
     * 时间转字符串
     *
     * @param date
     * @return
     */
    public static String dateToString(Date date) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        return df.format(date);
    }

    /**
     * 时间转字符串
     *
     * @param date
     * @return
     */
    public static String dateToString(Date date, SimpleDateFormat df) {
        return df.format(date);
    }

    /**
     * 获取指定的周一日期
     *
     * @param week -1表示上周，0表示当前周，1表示后面一周，类似
     * @return yulc
     */
    public static Date getMonday(Integer week) {
        Calendar cal = Calendar.getInstance();
        int w = (cal.get(Calendar.DAY_OF_WEEK) + 7 - 1) % 7;
        cal.add(Calendar.DAY_OF_YEAR, -((w + 7 - 1) % 7));
        cal.add(Calendar.WEEK_OF_YEAR, week);
        return cal.getTime();
    }

    /**
     * 获取某天的起始时间, e.g. 2005-10-01 00:00:00.000
     *
     * @param date 日期对象
     * @return 该天的起始时间
     */
    public static Date getStartDate(Date date) {
        if (date == null) {
            return null;
        }

        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return cal.getTime();
    }

    /**
     * 获取某天的结束时间, e.g. 2005-10-01 23:59:59.999
     *
     * @param date 日期对象
     * @return 该天的结束时间
     */
    public static Date getEndDate(Date date) {

        if (date == null) {
            return null;
        }

        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);

        return cal.getTime();
    }

    /** 调整 start */

    /**
     * 是否为微信3分钟临时放行
     *
     * @param from
     * @param to
     * @return
     */
    public static boolean isTemporaryWeixin(Date to, Date from) {
        if (to == null || from == null) {
            return false;
        }
        long t = compareSeconds(to, from);
        return t == 180 ? true : false;
    }

    /**
     * 是否为支付宝10分钟临时放行
     *
     * @param to
     * @param from
     * @return
     */
    public static boolean isTemporaryZhifubao(Date to, Date from) {
        if (to == null || from == null) {
            return false;
        }
        long t = compareSeconds(to, from);
        return t == 600 ? true : false;
    }

    public static long compareSeconds(Date d1, Date d2) {
        long x = d1.getTime() - d2.getTime();

        return x / 1000;
    }

    /** 调整 end */

    /**
     * 比较2个日期类型，返回2个日期相差的天数，正数表示前一个日期比后一个日期晚n天，负数表示前一日期比后一日期早
     *
     * @param d1
     * @param d2
     * @return
     */
    public static int compareDays(Date d1, Date d2) {
        if (d1 == null || d2 == null) {
            throw new RuntimeException("Not compare between d1<" + d1 + "> and d2<" + d2 + ">!");
        }
        d1 = trunc(d1);
        d2 = trunc(d2);
        int i = (int) ((d1.getTime() - d2.getTime()) / (24 * 3600 * 1000));
        return i;
    }

    private static boolean checkDay(int year, int month, int monthDay) {
        if (month < 0 || month > 11 || monthDay < 1 || monthDay > 31) {
            throw new RuntimeException("The day<" + year + "," + (month + 1) + "," + monthDay + "> is ERROR-DAY!");
        }
        Calendar cal = GregorianCalendar.getInstance();
        cal.set(year, month, 1);
        int maxDay = getMaxDaysOfMonth(cal.getTime());
        if (monthDay > maxDay) {
            throw new RuntimeException("The day<" + year + "," + (month + 1) + "," + monthDay + "> is ERROR-DAY!");
        }
        return true;
    }

    /**
     * 返回日期
     *
     * @param year
     * @param month 0-11
     * @param day   1-31
     * @return
     */
    public static Date toDate(int year, int month, int day) {
        checkDay(year, month, day);

        Calendar cal = GregorianCalendar.getInstance();
        cal.set(year, month, day, 0, 0, 0);

        return cal.getTime();
    }

    /**
     * 参数date在月份的大天数 如果传入的参数为null，则返回-1
     *
     * @param date
     * @return
     */
    public static int getMaxDaysOfMonth(Date date) {
        if (date == null) {
            return -1;
        }
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(date);
        return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 返回周几
     *
     * @param date
     * @return 一，二，三，，，，
     */
    public static String getDayOfChinaWeek(Date date) {
        String re = null;
        int today = DateUtil.getDayOfWeek(date);
        switch (today) {
            case 1:
                re = "日";
                break;
            case 2:
                re = "一";
                break;
            case 3:
                re = "二";
                break;
            case 4:
                re = "三";
                break;
            case 5:
                re = "四";
                break;
            case 6:
                re = "五";
                break;
            case 7:
                re = "六";
                break;
            default:
                break;
        }
        return re;
    }

    /**
     * 返回周几 returned value (<tt>1</tt> = Sunday, <tt>2</tt> = Monday, <tt>3</tt> = Tuesday, <tt>4</tt> = Wednesday,
     * <tt>6</tt> = Thursday, <tt>6</tt> = Friday, <tt>7</tt> = Saturday) 如果传入的参数为null，将抛出空指针异常�??
     *
     * @param date
     * @return
     */
    public static int getDayOfWeek(Date date) {
        Calendar cal = GregorianCalendar.getInstance();
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        cal.setTime(date);
        int result = cal.get(Calendar.DAY_OF_WEEK);
        return result;
    }

    /**
     * d0是否在[d1,d2]的日期区间中
     *
     * @param d0
     * @param d1
     * @param d2
     * @return
     * @throws NullPointerException if d0 or d1 or d2 is null.
     */
    public static boolean isDayInRange(Date d0, Date d1, Date d2) {
        return (compareDay(d0, d1) >= 0 && compareDay(d1, d2) <= 0 && compareDay(d0, d2) <= 0) ? true : false;
    }

    /**
     * 比较日期：年月日
     *
     * @param d0
     * @param d1
     * @return the value <code>0</code> if the argument d1 is equal to d0; a value less than <code>0</code> if d0 is
     * before d1 argument; and a value greater than <code>0</code> if d0 is after d1 argument.
     * @throws NullPointerException if d0 or d1 is null.
     */
    public static int compareDay(Date d0, Date d1) {
        return d0.compareTo(d1);
    }

    public static int compareToday(Date d0) {
        return d0.compareTo(new Date());
    }

    /**
     * 计算两个时间的间隔时间，格式：几天几小时几分
     *
     * @param from
     * @param to
     * @return
     */
    public static String calculateInterval(Date from, Date to) {
        if (from == null || to == null) {
            return "";
        }
        return calculateInterval(from.getTime() - to.getTime());
    }

    /**
     * 根据毫秒数计算时间间隔，格式：几天几小时几分
     *
     * @param timeMillisBetweenTwoDate
     * @return
     */
    public static String calculateInterval(long timeMillisBetweenTwoDate) {
        if (timeMillisBetweenTwoDate == 0) {
            return "0分钟";
        }
        StringBuilder str = new StringBuilder();
        long p = Math.abs(timeMillisBetweenTwoDate);
        long day = p / (24 * 3600000);
        if (day > 0) {
            str.append(day).append("天");
        }
        p = p % (24 * 3600000);
        long hour = p / (3600000);
        if (hour > 0) {
            str.append(hour).append("小时");
        }
        p = p % (3600000);
        long minute = p / (60000);
        if (minute > 0) {
            str.append(minute).append("分钟");
        }
        p = p % (60000);
        long second = p / (1000);
        if (second > 0) {
            str.append(second).append("秒");
        }
        return str.toString();
    }

    /**
     * 某年某月某日的所在周的第�?�?(周一)�? 如果传入的参数为null则返回null�?
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfWeek(Date date) {
        if (date == null) {
            return null;
        }
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_WEEK, 1 - getDayOfWeekForChina(date));

        return cal.getTime();
    }

    /**
     * 返回周几 returned value (<tt>7</tt> = Sunday, <tt>1</tt> = Monday, <tt>2</tt> = Tuesday, <tt>3</tt> = Wednesday,
     * <tt>4</tt> = Thursday, <tt>5</tt> = Friday, <tt>6</tt> = Saturday) 如果传入的参数为null，将抛出空指针异常�??
     *
     * @param date
     * @return
     */
    private static int getDayOfWeekForChina(Date date) {
        Calendar cal = GregorianCalendar.getInstance();
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        cal.setTime(date);
        int result = cal.get(Calendar.DAY_OF_WEEK);
        if (result == 1) {
            return 7;
        } else {
            return result - 1;
        }
    }

    /**
     * 创建人: yulc<br/>
     * 创建时间：2013-10-30 上午10:27:28<br/>
     * 方法描述：认证时间文本内容<br/>
     *
     * @param d1 较晚的时间
     * @param d2 较早的时间
     * @return
     */
    public static String getAuthTime(Date d1, Date d2) {
        String str = "";
        long minutes = (d1.getTime() - d2.getTime()) / 1000 / 60;
        long hour = minutes / 60;
        long minute = 0;
        if (minutes % 60 != 0) {
            minute = minutes % 60;
        }
        if (hour > 0) {
            str += hour + "小时";
        }
        if (minute > 0) {
            str += minute + "分钟";
        }
        return str;
    }

    /**
     * 比较传入日期与当前系统时间相差的天数，返回2个日期相差的天数，正数表示前一个日期比后一个日期晚n天，负数表示前一日期比后一日期早
     *
     * @param d1
     * @return
     */
    public static int compareDaysWithNow(Date d1) {
        if (d1 == null) {
            throw new RuntimeException("Not compare of null value");
        }
        d1 = trunc(d1);
        Date d2 = trunc(new Date());
        int i = (int) ((d1.getTime() - d2.getTime()) / (24 * 3600 * 1000));
        return i;
    }

    public static long getCurrentTimeMsec() {

        Date date = new Date();

        return date.getTime();

    }

    /**
     * 获取日期和时间
     *
     * @return 返回格式 yyyyMMddHHmmss 如 20150601180101
     */
    public static String getCurrentDate() {
        Date curDate = new Date();
        SimpleDateFormat simple = new SimpleDateFormat(DATE_TIME_FORMAT);
        String dateRet = simple.format(curDate).replace(" ", "");
        return dateRet;
    }

    /**
     * 获取日期
     *
     * @return 返回格式 yyyyMMdd 如 20150601
     */
    public static String getCurrentDay() {
        Date curDate = new Date();
        SimpleDateFormat simple = new SimpleDateFormat(ISO_DATE_FORMAT);
        String dateRet = simple.format(curDate);
        return dateRet;
    }

    /**
     * 返回当天的最后一毫秒
     *
     * @param date <code>java.util.Date</code>
     * @return 截取后的<code>java.util.Date</code>
     */
    public static Date dayEndByMillisecond(Date date) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(date);
        Calendar c2 = (Calendar) c1.clone();
        c1.clear();
        c1.set(Calendar.YEAR, c2.get(Calendar.YEAR));
        c1.set(Calendar.MONTH, c2.get(Calendar.MONTH));
        c1.set(Calendar.DAY_OF_MONTH, c2.get(Calendar.DAY_OF_MONTH));
        // 23:59:59.999
        c1.set(Calendar.HOUR_OF_DAY, 23);
        c1.set(Calendar.MINUTE, 59);
        c1.set(Calendar.SECOND, 59);
        c1.set(Calendar.MILLISECOND, 999);
        return c1.getTime();
    }

    /**
     * 往后推迟num天
     *
     * @param start
     * @param num
     * @param unit  Calendar.DATE Calendar.MINUTE Calendar.HOUR Calendar.MONTH
     * @return 推迟后的date
     */
    public static Date dateAdd(Date start, int num, int unit) {
        Calendar c = Calendar.getInstance();
        c.setTime(start);
        switch (unit) {
            case Calendar.DATE:
                c.set(Calendar.DATE, c.get(Calendar.DATE) + num);
                break;
            case Calendar.MINUTE:
                c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + num);
                break;
            case Calendar.HOUR:
                c.set(Calendar.HOUR, c.get(Calendar.HOUR) + num);
                break;
            case Calendar.MONTH:
                c.set(Calendar.MONTH, c.get(Calendar.MONTH) + num);
                break;
            default:
                c.set(Calendar.MONTH, c.get(Calendar.MONTH) + num);
                break;
        }
        return c.getTime();
    }

    /**
     * 将字符串日期解析成日期对象
     *
     * @param date
     * @param format 取值如：DateUtil.YYMMDD
     * @return
     */
    public static Date parse(String date, String format) {
        if (StringUtils.isEmpty(date)) {
            return null;
        }
        SimpleDateFormat formater = new SimpleDateFormat(format);
        try {
            return formater.parse(date);
        } catch (ParseException e) {
            return null;
        }
    }
}

